﻿using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.DataManagementTools;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geoprocessor;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GenerateSymbolPointsAddin
{
    public partial class MyForm : Form
    {
        public MyForm()
        {
            InitializeComponent();
        }

        private IMxDocument _mxDoc = null;

        //private IWorkspace _defaultWorkspace = null;

        //private IWorkspace _currentWorkspace = null;

        private Dictionary<string, IFeatureLayer> _featureLayerDic = null;

        private Dictionary<string, string> _datasourceDic = null;

        private Thread _thread = null;

        private void MyForm_Load(object sender, EventArgs e)
        {
            InitMyForm();
        }
        /// <summary>
        /// 初始化窗口
        /// </summary>
        private void InitMyForm()
        {
            this.comboBox_layer.Items.Clear();
            this.comboBox_layer.Text = "";
            _mxDoc = ArcMap.Document;
            int layerCount = _mxDoc.FocusMap.LayerCount;
            _featureLayerDic = new Dictionary<string, IFeatureLayer>();
            _datasourceDic = new Dictionary<string, string>();
            for (int i = 0; i < layerCount; i++)
            {
                ILayer layer = _mxDoc.FocusMap.get_Layer(i);
                IFeatureLayer pFeatureLayer = layer as IFeatureLayer;
                IDataLayer pDLayer = layer as IDataLayer;
                if (pFeatureLayer != null && pDLayer != null)
                {
                    IWorkspaceName ws = ((IDatasetName)(pDLayer.DataSourceName)).WorkspaceName;
                    string srcPath = ws.PathName;
                    string layerName = pFeatureLayer.Name;
                    this.comboBox_layer.Items.Add(layerName);
                    _featureLayerDic.Add(layerName, pFeatureLayer);
                    _datasourceDic.Add(layerName, srcPath);
                }
            }
        }
       

        private void comboBox_layer_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.comboBox_field.Items.Clear();
            this.comboBox_field.Text = "";
            string layerName = this.comboBox_layer.SelectedItem.ToString();
            if (_featureLayerDic != null && _featureLayerDic.Keys.Contains(layerName))
            {
                IFeatureLayer pFeatureLayer = _featureLayerDic[layerName];
                IFields pFields = pFeatureLayer.FeatureClass.Fields;
                for (int i = 0; i < pFields.FieldCount; i++)
                {
                    IField pField = pFields.get_Field(i);
                    string fieldName = pField.Name;
                    if (fieldName == "DLBM")
                    {
                        this.comboBox_field.Text = "DLBM";
                    }
                    this.comboBox_field.Items.Add(fieldName);
                }
            }
        }


        private void button_generate_Click(object sender, EventArgs e)
        {
            string layerName = this.comboBox_layer.Text.Trim();
            string fieldName = this.comboBox_field.Text.Trim();
            string scaleValue = this.comboBox_scale.Text.Trim();
            string x_interval_text = this.textBox_hdis.Text.Trim();
            string y_interval_text = this.textBox_vdis.Text.Trim();
            if (layerName == "" || fieldName == "" || scaleValue == "" || x_interval_text == "" || y_interval_text == "")
            {
                MessageBox.Show(this, "请选择图层和地类编码字段，并设置必要参数。","提示",MessageBoxButtons.OK,MessageBoxIcon.Warning);
                return;
            }

            try
            {
                this.Cursor = System.Windows.Forms.Cursors.WaitCursor;//鼠标为忙碌状态

                double scale = Double.Parse(scaleValue);
                double x_interval = Double.Parse(x_interval_text);
                double y_interval = Double.Parse(y_interval_text);
                double x_interval_m = x_interval * scale * 0.001;
                double y_interval_m = y_interval * scale * 0.001;
                double threshold = 2.0 * scale * 0.001;

                string myDocPath = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                string defaultGdb = myDocPath + @"\ArcGIS\Default.gdb";
                IWorkspaceFactory defaultFactory = new FileGDBWorkspaceFactoryClass();
                IWorkspace _defaultWorkspace = defaultFactory.OpenFromFile(defaultGdb, 0);

                //打开选中图层所在数据库
                string currentGdb = _datasourceDic[layerName];
                IWorkspaceFactory workspaceFactory = new FileGDBWorkspaceFactoryClass();
                IWorkspace _currentWorkspace = workspaceFactory.OpenFromFile(currentGdb, 0);
                if (_defaultWorkspace == null || _currentWorkspace == null)
                {
                    MessageBox.Show(this, "打开数据库工作空间失败。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                //获取选中图层
                IFeatureLayer pFeatureLayer = _featureLayerDic[layerName];
                IGeoDataset geoDataset = (IGeoDataset)pFeatureLayer;
                ISpatialReference spatialReference = geoDataset.SpatialReference;
                IFeatureClass featureClass = pFeatureLayer.FeatureClass;
                IFeatureCursor featureCursor = featureClass.Search(null, true);//用于遍历要素的游标

                //创建网格点数据集
                IFeatureClass gridFeatureClass = CreateGridFeatureClass(_currentWorkspace, spatialReference);
                IFeatureCursor gridFeatureCursor = gridFeatureClass.Insert(true);//用于插入要素的游标
                //创建临时图层，用于存储没有网格点的图形
                string tempName = "tempLayer";
                string tempLayerFeatureToPoint = "tempLayer_FeatureToPoint";
                DeleteDataset(_defaultWorkspace, tempName);//删除临时数据集
                DeleteDataset(_defaultWorkspace, tempLayerFeatureToPoint);//删除临时数据集
                IFeatureClass tempFeatureClass = CreateFeatureClass(_defaultWorkspace, tempName, featureClass.Fields);
                //IFeatureClass tempFeatureClass = CreateTempFeatureClass(_currentWorkspace, spatialReference);
                IFeatureCursor tempFeatureCursor = tempFeatureClass.Insert(true);//用于插入要素的游标

                progressBar1.Minimum = 0;
                progressBar1.Maximum = featureClass.FeatureCount(null);

                int row = 1;
                IFeature feature = null;
                while ((feature = featureCursor.NextFeature()) != null)
                {
                    progressBar1.Value += 1;
                    
                    string dlbm = feature.get_Value(feature.Fields.FindField(fieldName)).ToString();
                   
                    //不填充地类符号的地类编码
                    if (dlbm == "0603" || dlbm == "0701" || dlbm == "0702" || dlbm == "1101" || dlbm == "1102" || dlbm == "1103" || dlbm == "1104")
                    {
                        continue;
                    }
                    else if (dlbm.Substring(0, 2) == "05" || dlbm == "0601" || dlbm == "0602" || dlbm.Substring(0, 2) == "08" || dlbm.Substring(0, 2) == "09" || dlbm.Substring(0, 2) == "10" || dlbm == "1107" || dlbm == "1109" || dlbm == "1201" || dlbm == "1202" || dlbm == "1203")
                    {
                        //单独符号的地类编码
                        IFeatureBuffer featureBuffer = feature as IFeatureBuffer;
                        //featureBuffer.Shape = feature.ShapeCopy;
                        //featureBuffer.set_Value(featureBuffer.Fields.FindField(fieldName), dlbm);
                        tempFeatureCursor.InsertFeature(featureBuffer);
                        tempFeatureCursor.Flush();
                        continue;
                    }

                    IGeometry pGeo = feature.ShapeCopy;
                    ITopologicalOperator pTopo = pGeo as ITopologicalOperator;
                    IGeometry bufferGeom = pTopo.Buffer(-threshold);
                    IRelationalOperator relationalOperator = bufferGeom as IRelationalOperator;

                    if (dlbm == "0301" || dlbm == "0307")
                    {
                        x_interval_m = 0.5 * x_interval * scale * 0.001;
                    }
                    else
                    {
                        x_interval_m = x_interval * scale * 0.001;
                    }
                    double exDis = 2 * scale * 0.001;

                    IEnvelope envelope = feature.Extent;
                    double xmin = envelope.XMin - exDis;
                    double xmax = envelope.XMax + exDis;
                    double ymin = envelope.YMin - exDis;
                    double ymax = envelope.YMax + exDis;

                    int containsPoint = 0;

                    for (double y = ymin; y <= ymax; y += y_interval_m)
                    {
                        for (double x = xmin; x <= xmax; x += x_interval_m)
                        {
                            double dx = x;
                            if (row % 2 == 0)
                            {
                                if (dlbm != "0301" && dlbm != "0307")
                                {
                                    dx = x - x_interval_m * 0.5;
                                }

                            }

                            IPoint point = new PointClass() as IPoint;
                            point.PutCoords(dx, y);

                            if (relationalOperator.Contains(point))
                            {
                                IFeatureBuffer featureBuffer = gridFeatureClass.CreateFeatureBuffer();
                                featureBuffer.Shape = point;
                                featureBuffer.set_Value(featureBuffer.Fields.FindField("DLBM"), dlbm);
                                gridFeatureCursor.InsertFeature(featureBuffer);
                                gridFeatureCursor.Flush();
                                containsPoint++;
                            }
                        }
                        row++;
                    }
                    if (containsPoint == 0)
                    {
                        //单独计算图形内的质心点，保证每个图形至少有一个符号点
                        IFeatureBuffer featureBuffer = feature as IFeatureBuffer; 
                        featureBuffer.Shape = feature.ShapeCopy;
                        featureBuffer.set_Value(featureBuffer.Fields.FindField(fieldName), dlbm);
                        tempFeatureCursor.InsertFeature(featureBuffer);
                        tempFeatureCursor.Flush();
                    }
                }

                Marshal.ReleaseComObject(featureCursor);
                Marshal.ReleaseComObject(tempFeatureCursor);

                //将没有网格点的图形转为点，保证每个图形有一个地类符号
                IFeatureLayer inFeatureLayer = new FeatureLayerClass();
                inFeatureLayer.FeatureClass = tempFeatureClass;
                inFeatureLayer.Name = tempFeatureClass.AliasName;
                IFeatureClass outFeatureClass = null;

                FeatureToPoint featureToPoint = new FeatureToPoint(inFeatureLayer, outFeatureClass);
                featureToPoint.point_location = "INSIDE";
                Geoprocessor GP_Tool = new Geoprocessor();//GP运行工具  
                GP_Tool.Execute(featureToPoint, null);
                //打开tempLayer_FeatureToPoint 
                IFeatureWorkspace defaultFeatureWorkspace = _defaultWorkspace as IFeatureWorkspace;
                outFeatureClass = defaultFeatureWorkspace.OpenFeatureClass(tempLayerFeatureToPoint);
                if (outFeatureClass != null)
                {
                    IFeatureCursor outfeatureCursor = outFeatureClass.Search(null, true);//用于遍历要素的游标
                    IFeature outfeature = null;
                    while ((outfeature = outfeatureCursor.NextFeature()) != null)
                    {
                        string dlbm = outfeature.get_Value(outfeature.Fields.FindField(fieldName)).ToString();
                        IFeatureBuffer featureBuffer = gridFeatureClass.CreateFeatureBuffer();
                        featureBuffer.Shape = outfeature.ShapeCopy;
                        featureBuffer.set_Value(featureBuffer.Fields.FindField("DLBM"), dlbm);
                        gridFeatureCursor.InsertFeature(featureBuffer);
                        gridFeatureCursor.Flush();
                    }
                    Marshal.ReleaseComObject(outfeatureCursor);
                }

                Marshal.ReleaseComObject(gridFeatureCursor);

                DeleteDataset(_defaultWorkspace, tempName);//删除临时数据集
                DeleteDataset(_defaultWorkspace, tempLayerFeatureToPoint);//删除临时数据集

                //将图层添加到地图列表
                AddLayerToFocusMap(gridFeatureClass);
                this.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                this.Cursor = System.Windows.Forms.Cursors.Arrow;//设置鼠标为正常状态
            }
        }


        private void button_close_Click(object sender, EventArgs e)
        {
            if (_thread != null)
                _thread.Abort();
            this.Close();
        }


        /// <summary>
        /// 创建esriFieldTypeString属性字段
        /// </summary>
        /// <param name="name"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static IField CreateStringField(string name, int length)
        {
            var field = new Field();
            var fieldEdit = (IFieldEdit)field;
            fieldEdit.Name_2 = name;
            fieldEdit.Type_2 = esriFieldType.esriFieldTypeString;
            fieldEdit.Length_2 = length;
            return field;
        }
        /// <summary>
        /// 创建esriFieldTypeDouble属性字段
        /// </summary>
        /// <param name="name"></param>
        /// <param name="precision"></param>
        /// <returns></returns>
        public static IField CreateDoubleField(string name, int precision)
        {
            var field = new Field();
            var fieldEdit = (IFieldEdit)field;
            fieldEdit.Name_2 = name;
            fieldEdit.Type_2 = esriFieldType.esriFieldTypeDouble;
            //fieldEdit.Precision_2 = precision;
            return field;
        }
        /// <summary>
        /// 创建几何字段
        /// </summary>
        /// <param name="projection"></param>
        /// <returns></returns>
        public static IField CreateGeometryField(ISpatialReference spatialReference,esriGeometryType geoType)
        {
            // 创建几何定义
            var geometryDef = new GeometryDef();
            var geometryDefEdit = (IGeometryDefEdit)geometryDef;
            geometryDefEdit.GeometryType_2 = geoType;
            // 创建空间参考
            geometryDefEdit.SpatialReference_2 = spatialReference;

            // 创建字段
            var geometryField = new Field();
            var geometryFieldEdit = (IFieldEdit)geometryField;
            geometryFieldEdit.Name_2 = "Shape";// 字段名按惯例叫Shape即可，与CreateFeatureClass中必须一致
            geometryFieldEdit.Type_2 = esriFieldType.esriFieldTypeGeometry;// 字段类型Geometry
            geometryFieldEdit.GeometryDef_2 = geometryDef;

            return geometryField;
        }
        /// <summary>
        /// 创建要素集
        /// </summary>
        /// <param name="workspace"></param>
        /// <param name="file"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        public static IFeatureClass CreateFeatureClass(IWorkspace workspace, string file, IFields fields)
        {
            if (workspace == null)
                return null;
            // 取得要素工作空间
            var featureWorkspace = workspace as IFeatureWorkspace;

            // 创建要素类
            var featureClass = featureWorkspace.CreateFeatureClass(
                file,   // 名称，不用带后缀
                fields, // 要素类的字段，其中必须包含Geometry字段
                null,   // 指定要素类包含何种要素，一般null即可
                null,   // 指定何种类型将被实例化为要素类扩展的，一般null即可
                esriFeatureType.esriFTSimple, // 要素类型
                "Shape",// 几何字段的名称，按惯例叫Shape即可
                ""      // 允许应用程序控制RDBMS中的表结构，一般用空字符串即可
            );

            return featureClass;
        }

        /// <summary>
        /// 创建网格点数据集
        /// </summary>
        /// <param name="featureWorkspace"></param>
        /// <param name="spatialReference"></param>
        /// <returns></returns>
        public static IFeatureClass CreateGridFeatureClass(IWorkspace workspace, ISpatialReference spatialReference)
        {
            // 创建Geometry字段
            var geometryField = CreateGeometryField(spatialReference, esriGeometryType.esriGeometryPoint);
            var dlbmField = CreateStringField("DLBM", 50);

            // 创建字段集合
            var fields = new Fields();
            var fieldsEdit = (IFieldsEdit)fields;
            fieldsEdit.AddField(geometryField);
            fieldsEdit.AddField(dlbmField);

            string name = "DLFH";
            DeleteDataset(workspace, name);//如果存在，先删除
            return CreateFeatureClass(workspace, name, fields);
        }
        /// <summary>
        /// 创建网格点数据集
        /// </summary>
        /// <param name="featureWorkspace"></param>
        /// <param name="spatialReference"></param>
        /// <returns></returns>
        public static IFeatureClass CreateTempFeatureClass(IWorkspace workspace, ISpatialReference spatialReference)
        {
            // 创建Geometry字段
            var geometryField = CreateGeometryField(spatialReference, esriGeometryType.esriGeometryPolygon);
            var dlbmField = CreateStringField("DLBM", 50);

            // 创建字段集合
            var fields = new Fields();
            var fieldsEdit = (IFieldsEdit)fields;
            fieldsEdit.AddField(geometryField);
            fieldsEdit.AddField(dlbmField);

            string name = "tempLayer";
            DeleteDataset(workspace, name);//如果存在，先删除
            return CreateFeatureClass(workspace, name, fields);
        }

        /// <summary>
        /// 删除数据集中指定的要素集(先判断存在)
        /// </summary>
        /// <param name="workspace"></param>
        /// <param name="datasetName"></param>
        public static void DeleteDataset(IWorkspace workspace, string datasetName)
        {
            IEnumDatasetName datasetNames = workspace.get_DatasetNames(esriDatasetType.esriDTAny);
            IDatasetName dsName = null;
            while ((dsName = datasetNames.Next()) != null)
            {
                if (datasetName == dsName.Name)
                {
                    DeleteFeatureClass(workspace, datasetName);
                    break;
                }
                IEnumDatasetName subNames = dsName.SubsetNames;
                if (subNames != null)
                {
                    IDatasetName subName = null;
                    while ((subName = subNames.Next()) != null)
                    {
                        if (datasetName == subName.Name)
                        {
                            DeleteFeatureClass(workspace, datasetName);
                            break;
                        }
                    }
                }

            }
        }
        /// <summary>
        /// 删除要素集
        /// </summary>
        /// <param name="workspace"></param>
        /// <param name="name"></param>
        public static void DeleteFeatureClass(IWorkspace workspace, string name)
        {
            IFeatureWorkspace featureWorkspace = workspace as IFeatureWorkspace;
            IFeatureClass featureClass = featureWorkspace.OpenFeatureClass(name);//存在删除
            if (featureClass != null)
            {
                IDataset pDataset = featureClass as IDataset;
                if (pDataset.CanDelete())
                {
                    pDataset.Delete();
                }
            }
        }
        /// <summary>
        /// 将图层添加到当前地图
        /// </summary>
        /// <param name="featureClass"></param>
        private void AddLayerToFocusMap(IFeatureClass featureClass)
        {
            if (featureClass != null)
            {
                IFeatureLayer gridFeatureLayer = new FeatureLayerClass();
                gridFeatureLayer.FeatureClass = featureClass;
                gridFeatureLayer.Name = featureClass.AliasName;
                ILayer layer = gridFeatureLayer as ILayer;
                _mxDoc.FocusMap.AddLayer(layer);
                _mxDoc.FocusMap.RecalcFullExtent();
            }
        }
    }
}
